﻿using System;
using System.Collections.Generic;
using System.Threading;
using Neturion.Dados;
using Neturion.IRepositorios;
using Neturion.Repositorios;

namespace Neturion.Negocios
{
    public class NegocioPagamento
    {
        private IRepositorioPagamento _repositorioPagamento;
        private NegocioFormaPagamento _negocioFormaPagamento;
        private NegocioTitulo _negocioTitulo;
        private NegocioLancamento _negocioLancamento;

        public NegocioPagamento()
        {
            _repositorioPagamento = new RepositorioPagamento();
        }

        public Pagamento Inserir(Pagamento pagamento)
        {
            if (!ValidarPagamento(pagamento))
            {
                throw new Exception("Erro no Pagamento");
            }

            if (pagamento.Cheque != null)
            {
                pagamento.Cheque.AdicionarPagamento(pagamento);
            }
            return _repositorioPagamento.Inserir(pagamento);
        }

        public Pagamento Atualizar(Pagamento pagamento)
        {
            if (!ValidarPagamento(pagamento))
            {
                throw new Exception("Erro no Pagamento");
            }

            if (pagamento.Cheque != null)
            {
                pagamento.Cheque.AdicionarPagamento(pagamento);
            }

            return _repositorioPagamento.Atualizar(pagamento);
        }

        public Titulo EfetuarPagamento(Titulo titulo, Pagamento pagamento, Usuario usuarioPermissao)
        {
            Titulo tituloTmp;
            _negocioFormaPagamento = new NegocioFormaPagamento();
            _negocioTitulo = new NegocioTitulo();
            _negocioLancamento = new NegocioLancamento();


            //Clonar Titulo para Tirar Fixed Size das Listas
            tituloTmp = Utils.Util.Clonar(titulo, null, null);

            //Validar Pagamento
            if (!ValidarPagamento(pagamento))
            {
                throw new Exception("Pagamento Incorreto");
            }

            //Validar Data Pagamento com Data Aceitação do Titulo
            if (pagamento.DataPagamento < tituloTmp.MovimentoFinanceiro.DataAceitacao)
            {
                throw new Exception("Data do Pagamento Incorreto Menor que a data do Aceito");
            }

            //Checar a Validade do Prazo do Pagamento
            if ((tituloTmp.ValidadePagamento > 0) && ((pagamento.DataPagamento.GetValueOrDefault() - tituloTmp.DataVencimento.Date).Days > tituloTmp.ValidadePagamento))
            {
                throw new Exception("Pagamento Fora do Prazo de Validade Estipulado no Movimento");
            }

            //Se a Situação do Titulo for Diferente de Aberto ou ParcialmenteQuitado
            if ((titulo.EnumSituacaoMovimento == EnumSituacaoMovimento.Inserindo)
                || (titulo.EnumSituacaoMovimento == EnumSituacaoMovimento.Aceitado)
                || (titulo.EnumSituacaoMovimento == EnumSituacaoMovimento.Quitado)
                || (titulo.EnumSituacaoMovimento == EnumSituacaoMovimento.Extornado))
            {
                throw new Exception("Situação do Título não Permite Pagamentos");
            }

            //Se a Forma de Pagamento força o Vencimento coloca a data de Pagamento com a data do Vencimento forçado
            if (pagamento.FormaPagamento.ForcarVencimento)
            {
                pagamento.DataLancamentoPagamento =
                    _negocioFormaPagamento.CalcularDataVencimentoFormaPagamento(
                        tituloTmp.DataEmissao, pagamento.FormaPagamento);
            }
            else
            {
                pagamento.DataLancamentoPagamento = pagamento.DataPagamento.GetValueOrDefault().Date;
            }

            //Pagamento com cheque inclui o pagamento na lista do Cheque
            if (pagamento.Cheque != null)
            {
                pagamento.Cheque.AdicionarPagamento(pagamento);
            }

            //Usuário que fez o pagamento
            pagamento.Usuario = usuarioPermissao;

            //Situação do Pagamento (Quitado)
            pagamento.EnumSituacaoMovimento = EnumSituacaoMovimento.Quitado;

            //Adiciona o Pagamento Modificado na Lista de Pagamentos
            tituloTmp.AdicionarPagamento(pagamento);

            //Quitar o Titulo
            tituloTmp = _negocioTitulo.QuitarTitulo(tituloTmp, pagamento, usuarioPermissao);

            ///////////////Gerar Lançamentos//////////////////////
            //Pagamento de Acordo a Forma de Pagamento, para Efetuar o Pagamento Se for Encontro de Contas não gera Movimentação financeira $$
            NegocioContaBanco negocioContaBanco = new NegocioContaBanco();

            //1ºPagamento por Encontro de Contas
            if (pagamento.FormaPagamento.TipoFormaPagamento.EnumTipoFormaPagamento == EnumTipoFormaPagamento.EncontroContas)
            {
                //Se a Conta NÃO for do tipo encontro de contas exceção
                if (pagamento.ContaBanco.TipoContaBanco.EnumTipoContaBanco != EnumTipoContaBanco.ContaAuxiliar)
                {
                    throw new Exception("Tipo de Conta Inválida: Conta Banco NÃO é do Tipo Conta Auxiliar");
                }

                //Pagamento com Encontro de Contas
                //Não movimenta valores
                //Não Verifica Saldo - Só Credita e Debita na conta do Auxiliar CredorDevedor
                switch (titulo.EnumTipoMovimentoFinanceiro)
                {
                    //Inverte os Tipos Movimentos para gerar o Credito ou Debito na conta do Credor ou Devedor 
                    case EnumTipoMovimentoFinanceiro.Pagar:
                    {
                        //Crédito na Conta do CredorDevedor
                        negocioContaBanco.Creditar(pagamento.ContaBanco, pagamento.Valor);
                        _negocioLancamento.CriarLancamento(_negocioTitulo.UltimoPagamentoTitulo(tituloTmp), EnumTipoMovimentoFinanceiro.Receber);
                        break;
                    }

                    case EnumTipoMovimentoFinanceiro.Receber:
                    {
                        //Débito na Conta do CredorDevedor
                        negocioContaBanco.Debitar(pagamento.ContaBanco, pagamento.Valor);
                        _negocioLancamento.CriarLancamento(_negocioTitulo.UltimoPagamentoTitulo(tituloTmp), EnumTipoMovimentoFinanceiro.Pagar);
                        break;
                    }
                }
               
            }
            else// Depois outros tipos de Pagamentos
            {
                //Se a Conta FOR do tipo encontro de contas exceção
                if (pagamento.ContaBanco.TipoContaBanco.EnumTipoContaBanco == EnumTipoContaBanco.ContaAuxiliar)
                {
                    throw new Exception("Tipo de Conta Inválida: Conta Banco é do Tipo Conta Auxiliar");
                }

                //Pagamento Comun
                switch (titulo.EnumTipoMovimentoFinanceiro)
                {
                    //Verifica o saldo no Pagamento de Conta a Pagar
                    case EnumTipoMovimentoFinanceiro.Pagar:
                    {
                        //Débito na Conta da Empresa
                        negocioContaBanco.VerificarSaldo(pagamento.ContaBanco, pagamento.Valor);
                        negocioContaBanco.Debitar(pagamento.ContaBanco, pagamento.Valor);
                        _negocioLancamento.CriarLancamento(_negocioTitulo.UltimoPagamentoTitulo(tituloTmp), EnumTipoMovimentoFinanceiro.Pagar);
                        break;
                    }

                    case EnumTipoMovimentoFinanceiro.Receber:
                    {
                        //Crédito na Conta da Empresa
                        negocioContaBanco.Creditar(pagamento.ContaBanco, pagamento.Valor);
                        _negocioLancamento.CriarLancamento(_negocioTitulo.UltimoPagamentoTitulo(tituloTmp), EnumTipoMovimentoFinanceiro.Receber);
                        break;
                    }
                }
            }

            return tituloTmp;
        }

        public Titulo EstornarPagamento(Titulo titulo, Pagamento pagamento, Usuario usuarioPermissao)
        {
            Titulo tituloTmp;
            _negocioFormaPagamento = new NegocioFormaPagamento();
            _negocioTitulo = new NegocioTitulo();
            _negocioLancamento = new NegocioLancamento();

            //Clonar Titulo para Tirar Fixed Size das Listas
            tituloTmp = Utils.Util.Clonar(titulo, null, null);

            //Retira o Pagamento da Lista para Alteralo
            tituloTmp.RemoverPagamento(pagamento);

            //Validar Pagamento
            if (!ValidarPagamento(pagamento))
            {
                throw new Exception("Pagamento Incorreto");
            }

            //Não pode estornar Pagamento já estornado
            if (pagamento.EnumSituacaoMovimento == EnumSituacaoMovimento.Extornado)
            {
                throw new Exception("Pagamento Já Extornado");
            }

            /*
            //Estornar o Pagamento no Cheque
            if (pagamento.Cheque != null)
            {
                //Clonar Cheque para Tirar Fixed Size das Listas
                Cheque cheque = Utils.Util.Clonar(pagamento.Cheque, null, null);
                //Remove o pagamento do Cheque
                cheque.RemoverPagamento(pagamento);
                //Adiciona o Cheque sem Alterado sem o Pagamento para ser Alterado
                pagamento.Cheque = cheque;
            }
            */
            //Situação do Pagamento (Estornado)
            pagamento.EnumSituacaoMovimento = EnumSituacaoMovimento.Extornado;

            //Adiciona o Pagamento Modificado na Lista de Pagamentos
            tituloTmp.AdicionarPagamento(pagamento);

            tituloTmp = _negocioTitulo.AbrirTitulo(tituloTmp, pagamento, usuarioPermissao);


            //Verificar a Forma de Pagamento para Efetuar o Estorno do Pagamento Se for Encontro de Contas não gera Movimentação financeira
            NegocioContaBanco negocioContaBanco = new NegocioContaBanco();
            if (pagamento.FormaPagamento.TipoFormaPagamento.EnumTipoFormaPagamento == EnumTipoFormaPagamento.EncontroContas)
            {
                //Pagamento com Encontro de Contas
                //Não movimenta valores
                //Não Verifica Saldo - Só Credita e Debita na conta do Auxiliar CredorDevedor
                switch (titulo.EnumTipoMovimentoFinanceiro)
                {
                    //Inverte os Tipos Movimentos para gerar o Credito ou Debito na conta do Credor ou Devedor 
                    case EnumTipoMovimentoFinanceiro.Pagar:
                    {
                        //Débito na Conta do CredorDevedor
                        negocioContaBanco.Debitar(pagamento.ContaBanco, pagamento.Valor);
                        break;
                    }

                    case EnumTipoMovimentoFinanceiro.Receber:
                    {
                        //Crédito na Conta do CredorDevedor
                        negocioContaBanco.Creditar(pagamento.ContaBanco, pagamento.Valor);
                        break;
                    }
                }

            }
            else
            {
                //Pagamento Comun
                switch (titulo.EnumTipoMovimentoFinanceiro)
                {
                    case EnumTipoMovimentoFinanceiro.Pagar:
                    {
                        //Credito na Conta da Empresa
                        negocioContaBanco.Creditar(pagamento.ContaBanco, pagamento.Valor);
                        break;
                    }

                    //Verifica o saldo no Pagamento de Conta a Pagar
                    case EnumTipoMovimentoFinanceiro.Receber:
                    {
                        //Débito na Conta da Empresa
                        negocioContaBanco.VerificarSaldo(pagamento.ContaBanco, pagamento.Valor);
                        negocioContaBanco.Debitar(pagamento.ContaBanco, pagamento.Valor);
                        break;
                    }
                }
            }
            _negocioLancamento.CriarEstornoLancamento(pagamento);

            return tituloTmp;
        }

        public void Excluir(Pagamento pagamento)
        {
            Pagamento pagamentoTmp = ObterPorId(pagamento.PagamentoId);
            _repositorioPagamento.Excluir(pagamentoTmp);
        }

        public bool ValidarPagamento(Pagamento pagamento)
        {
            bool retorno = true;

            if (pagamento == null)
            {
                throw new Exception("Erro no Pagamento");
                retorno = false;
            }

            if (pagamento.Titulo == null)
            {
                throw new Exception("Erro no Título do Pagamento");
                retorno = false;
            }

            if (pagamento.DataPagamento == null)
            {
                throw new Exception("Erro na Data do Pagamento");
                retorno = false;
            }

            //FormaPagamento Obrigatoria
            if ((pagamento.FormaPagamento == null) || (pagamento.FormaPagamento.TipoFormaPagamento == null))
            {
                retorno = false;
                throw new Exception("Forma de Pagamento Obrigatória ou Incorreta");
            }

            //Se a forma de pagamento ta Incorreta
            if (pagamento.FormaPagamento.EnumTipoMovimentoFinanceiro != pagamento.Titulo.EnumTipoMovimentoFinanceiro)
            {
                retorno = false;
                throw new Exception("Forma de Pagamento não Permitida para o Tipo de Movimento!");
            }

            //Se a Conta não foi informada
            if (pagamento.ContaBanco == null)
            {
                throw new Exception("Conta Ausente");
            }

            return retorno;
        }

        public IList<Pagamento> ObterTodos()
        {
            return _repositorioPagamento.ObterTodos();
        }

        public Pagamento ObterPorId(int id)
        {
            return _repositorioPagamento.ObterPorId(id);
        }

        public decimal ObterPagamentosTitulo(Titulo titulo)
        {
            return _repositorioPagamento.ObterPagamentosTitulo(titulo);
        }
    }
}